#include <list.h>
#include <sync.h>
#include <proc.h>
#include <sched.h>
#include <assert.h>

void
wakeup_proc(struct proc_struct *proc) {
    assert(proc->state != PROC_ZOMBIE);
    bool intr_flag;
    local_intr_save(intr_flag);
    {
        if (proc->state != PROC_RUNNABLE) {
            proc->state = PROC_RUNNABLE;
            proc->wait_state = 0;
        }
        else {
            warn("wakeup runnable process.\n");
        }
    }
    local_intr_restore(intr_flag);
}

/**
 * schedule是操作系统中的调度函数，用于选择下一个要执行的进程并进行上下文切换。
 * 该函数首先检查当前进程是否需要重新调度，然后在进程列表中查找处于可运行状态的进程。
 * 如果找到可运行的进程，则选择该进程作为下一个要执行的进程，并进行上下文切换。
 * 如果没有找到可运行的进程，则选择空闲进程作为下一个要执行的进程。
 * 最后，恢复中断状态并退出调度函数。
 */
void
schedule(void) {
    // 保存中断状态标志
    bool intr_flag;
    // 定义指向进程列表项的指针
    list_entry_t *le, *last;
    // 定义下一个要执行的进程指针，并初始化为NULL
    struct proc_struct *next = NULL;
    // 保存当前中断状态，并禁止中断
    local_intr_save(intr_flag);
    {
        // 标记当前进程不需要重新调度
        current->need_resched = 0;
         // 确定进程列表的最后一个元素
        last = (current == idleproc) ? &proc_list : &(current->list_link);
         // 从最后一个元素开始遍历进程列表
        le = last;
        do {
            // 如果不是进程列表的末尾，则继续查找下一个可运行的进程
            if ((le = list_next(le)) != &proc_list) {
                 // 获取当前列表项对应的进程结构体
                next = le2proc(le, list_link);
                // 如果进程处于可运行状态，则停止查找
                if (next->state == PROC_RUNNABLE) {
                    break;
                }
            }
        } while (le != last);
         // 如果没有找到可运行的进程，则选择空闲进程作为下一个要执行的进程
        if (next == NULL || next->state != PROC_RUNNABLE) {
            next = idleproc;
        }
         // 增加下一个要执行的进程的运行次数
        next->runs ++;
        // 如果下一个要执行的进程不是当前进程，则进行上下文切换
        if (next != current) {
            proc_run(next);
        }
    }
     // 恢复中断状态
    local_intr_restore(intr_flag);
}

